/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.bpm.engine.impl.event.logger.handler;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.je.bpm.engine.delegate.event.ActivitiVariableEvent;
import com.je.bpm.engine.impl.variable.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public abstract class VariableEventHandler extends AbstractDatabaseEventLoggerEventHandler {

    private static final Logger logger = LoggerFactory.getLogger(VariableEventHandler.class);

    public static final String TYPE_BOOLEAN = "boolean";
    public static final String TYPE_STRING = "string";
    public static final String TYPE_SHORT = "short";
    public static final String TYPE_INTEGER = "integer";
    public static final String TYPE_DOUBLE = "double";
    public static final String TYPE_LONG = "long";
    public static final String TYPE_DATE = "date";
    public static final String TYPE_UUID = "uuid";
    public static final String TYPE_JSON = "json";

    protected Map<String, Object> createData(ActivitiVariableEvent variableEvent) {
        Map<String, Object> data = new HashMap<String, Object>();
        putInMapIfNotNull(data, Fields.NAME, variableEvent.getVariableName());
        putInMapIfNotNull(data, Fields.PROCESS_DEFINITION_ID, variableEvent.getProcessDefinitionId());
        putInMapIfNotNull(data, Fields.PROCESS_INSTANCE_ID, variableEvent.getProcessInstanceId());
        putInMapIfNotNull(data, Fields.EXECUTION_ID, variableEvent.getExecutionId());
        putInMapIfNotNull(data, Fields.VALUE, variableEvent.getVariableValue());

        VariableType variableType = variableEvent.getVariableType();
        if (variableType instanceof BooleanType) {

            putInMapIfNotNull(data, Fields.VALUE_BOOLEAN, (Boolean) variableEvent.getVariableValue());
            putInMapIfNotNull(data, Fields.VALUE, variableEvent.getVariableValue());
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_BOOLEAN);

        } else if (variableType instanceof StringType || variableType instanceof LongStringType) {

            putInMapIfNotNull(data, Fields.VALUE_STRING, (String) variableEvent.getVariableValue());
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_STRING);

        } else if (variableType instanceof ShortType) {

            Short value = (Short) variableEvent.getVariableValue();
            putInMapIfNotNull(data, Fields.VALUE_SHORT, value);
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_SHORT);

            if (value != null) {
                putInMapIfNotNull(data, Fields.VALUE_INTEGER, value.intValue());
                putInMapIfNotNull(data, Fields.VALUE_LONG, value.longValue());
                putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value.doubleValue());
            }

        } else if (variableType instanceof IntegerType) {

            Integer value = (Integer) variableEvent.getVariableValue();
            putInMapIfNotNull(data, Fields.VALUE_INTEGER, value);
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_INTEGER);

            if (value != null) {
                putInMapIfNotNull(data, Fields.VALUE_LONG, value.longValue());
                putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value.doubleValue());
            }

        } else if (variableType instanceof LongType) {

            Long value = (Long) variableEvent.getVariableValue();
            putInMapIfNotNull(data, Fields.VALUE_LONG, value);
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_LONG);

            if (value != null) {
                putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value.doubleValue());
            }

        } else if (variableType instanceof DoubleType) {

            Double value = (Double) variableEvent.getVariableValue();
            putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value);
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_DOUBLE);

            if (value != null) {
                putInMapIfNotNull(data, Fields.VALUE_INTEGER, value.intValue());
                putInMapIfNotNull(data, Fields.VALUE_LONG, value.longValue());
            }

        } else if (variableType instanceof DateType) {

            Date value = (Date) variableEvent.getVariableValue();
            putInMapIfNotNull(data, Fields.VALUE_DATE, value != null ? value.getTime() : null);
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_DATE);

        } else if (variableType instanceof UUIDType) {

            String value = null;
            if (variableEvent.getVariableValue() instanceof UUID) {
                value = ((UUID) variableEvent.getVariableValue()).toString();
            } else {
                value = (String) variableEvent.getVariableValue();
            }

            putInMapIfNotNull(data, Fields.VALUE_UUID, value);
            putInMapIfNotNull(data, Fields.VALUE_STRING, value);
            putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_UUID);

        } else if (variableType instanceof SerializableType || (variableEvent.getVariableValue() != null && (variableEvent.getVariableValue() instanceof Object))) {

            // Last try: serialize it to json
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                String value = objectMapper.writeValueAsString(variableEvent.getVariableValue());
                putInMapIfNotNull(data, Fields.VALUE_JSON, value);
                putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_JSON);
                putInMapIfNotNull(data, Fields.VALUE, value);
            } catch (JsonProcessingException e) {
                // Nothing to do about it
                logger.debug("Could not serialize variable value " + variableEvent.getVariableValue());
            }

        }

        return data;
    }

}
